package com.sihan.framework.etm.exception;

import com.framework.exception.AuthException;
import com.framework.exception.BusinessException;
import com.framework.model.ApiResult;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import com.framework.util.IpUtils;
import com.framework.util.JsonUtils;
import com.framework.util.WebUtils;
import com.framework.util.date.DatePattern;
import com.framework.util.date.DateUtils;
import com.framework.web.captcha.exception.CaptchaException;
import com.framework.web.captcha.exception.CaptchaTimeOutException;
import com.framework.web.websocket.WebSocketServer;
import com.sihan.framework.etm.entity.Log;
import com.sihan.framework.etm.entity.User;
import com.sihan.framework.etm.enums.ErrorType;
import com.sihan.framework.etm.enums.RoleType;
import com.sihan.framework.etm.service.LogService;
import com.sihan.framework.etm.service.RoleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ConcurrentAccessException;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.annotation.Resource;

@Slf4j
@RestControllerAdvice
@SuppressWarnings("rawtypes")
public class GlobalExceptionHandle {

  @Resource
  private LogService logService;
  @Resource
  private RoleService roleService;

  /**
   * 身份验证异常
   */
@ExceptionHandler({AuthenticationException.class, AuthException.class})
  public ApiResult authenticationException(Exception e) {
    if (e instanceof UnknownAccountException || e instanceof IncorrectCredentialsException) {
      return ApiResult.fail("用户名或密码错误");
    } else if (e instanceof LockedAccountException || e instanceof ExcessiveAttemptsException) {
      return ApiResult.fail("超过了尝试登录的次数，您的账户已经被锁定。");
    } else if (e instanceof DisabledAccountException) {
      return ApiResult.fail("用户被禁用");
    } else if (e instanceof ConcurrentAccessException) {
      return ApiResult.fail("用户已登录,不允许多地登录");
    } else if(e instanceof AuthException) {
      return ApiResult.fail(e.getMessage());
    } else {
      log.error("登录失败,未知错误", e);
      return ApiResult.fail("登录失败,联系管理员");
    }
  }

  /**
   * 授权验证异常
   */
  @ExceptionHandler(AuthorizationException.class)
  public ApiResult authorizationException(AuthorizationException e) {
    if (e instanceof UnauthenticatedException) {
      return ApiResult.fail("没有登录");
    }

    User user = WebUtils.user();
    //请求的IP
    String ip = IpUtils.getRequestIp(WebUtils.request());
    LocalDateTime localDateTime = LocalDateTime.now();
    String opera = "越权访问";
    StringBuffer content = new StringBuffer();
    try {
      content.append("【").append(user.getUsername()).append("】").append(opera);
      content.append("： “").append(user.getUsername()).append("” ").append(DateUtils.formatDateTime(localDateTime, DatePattern.DATETIME))
              .append(" 在IP地址为<").append(ip).append(">的客户端进行 “").append(opera).append("” 操作失败");
    } catch (Exception e1) {
    	log.error("异常",e1);
    }

    Log log = new Log(ip, opera, content.toString(), false, 2, ErrorType.CRITICAL.getValue(), user.getUsername(), new Date());
    logService.insert(log);
    sendErrorMsg(log);
    return ApiResult.fail("没有权限");
  }

  /**
   * 数据验证异常
   */
  @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
  public ApiResult bindException(Exception e) {
    BindingResult bindingResult;
    if (e instanceof BindException) {
      bindingResult = ((BindException) e).getBindingResult();
    } else {
      bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
    }
    Iterator<FieldError> iterator = bindingResult.getFieldErrors().iterator();
    if (!iterator.hasNext()) {
      return ApiResult.fail("[]");
    }
    StringBuilder sb = new StringBuilder();
    sb.append('[');
    while (true) {
      FieldError fieldError = iterator.next();
      sb.append(fieldError.getField()).append(fieldError.getDefaultMessage());
      if (!iterator.hasNext()) {
        return ApiResult.fail(sb.append(']').toString());
      }
      sb.append(',').append(' ');
    }
  }

  @ExceptionHandler(HttpMessageNotReadableException.class)
  public ApiResult httpMessageNotReadableException(Exception e) {
    e.printStackTrace();
    return ApiResult.fail("传入数据有误");
  }

  /**
   * 业务异常
   */
  @ExceptionHandler(BusinessException.class)
  public ApiResult businessException(BusinessException e) {
    log.error("[全局异常] - 业务异常", e);
    return ApiResult.fail(e.getMessage());
  }

  /**
   * 空指针异常
   */
  @ExceptionHandler(NullPointerException.class)
  public ApiResult nullPointerException(NullPointerException e) {
    log.error("[全局异常] - 空指针异常", e);
    return ApiResult.fail("空指针异常,请联系管理员");
  }

  /**
   * 运行时异常
   */
  @ExceptionHandler(RuntimeException.class)
  public ApiResult runtimeException(RuntimeException e) {
    log.error("[全局异常] - 运行时异常", e);
    return ApiResult.fail(e.getMessage());
  }

  /**
   * 验证码异常
   * @param e
   * @return
   */
  @ExceptionHandler(CaptchaException.class)
  public ApiResult captchaException(CaptchaException e) {
    if(e instanceof CaptchaTimeOutException) {
      return ApiResult.fail("验证码已过期");
    } else {
      return ApiResult.fail("验证码错误");
    }

  }

  /**
   * 未知异常
   */
  @ExceptionHandler(Exception.class)
  public ApiResult exception(Exception e) {
    log.error("[全局异常] - 未知异常", e);
    return ApiResult.fail(e.getMessage());
  }

  private void sendErrorMsg(Log log1) {
    CopyOnWriteArraySet<WebSocketServer> webSocketSet = WebSocketServer.getWebSocketSet();
    if(webSocketSet != null && webSocketSet.size() > 0) {
      for (WebSocketServer ws: webSocketSet) {
        if(ws.getSession().isOpen()) {
          Set<String> roles = roleService.selectRolesByUsername(ws.getSid());
          if(roles != null && roles.contains(RoleType.AUDITMANAGER.getValue())) {
            try {
              ws.sendMessage(JsonUtils.toJsonString(log1));
            } catch (IOException e) {
            	log.error("异常",e);
            }
          }
        }
      }
    }
  }

  @ExceptionHandler(AuthorizationOperatorException.class)
  public ApiResult authorizationOperatorException(AuthorizationOperatorException e) {
	   return ApiResult.fail("您没有操作权限-operator");
  }

}
